package server;

import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.io.*;

import communication.ClientDisconnect;
import communication.ComCliSrv;
/**
 * Classe representant un client dans une communication client-serveur.
 * 
 * Les clients multiples sont geres
 * 
 * La communication se base sur l'idee que seuls des objets sont transmis entre 
 * le client et le serveur.
 * lors de la reception d'un objet, celui-ci est transmis aux differents
 * observateurs de cette classe.
 * 
 * @author Julien Roch
 * @version 1.0
 */
public class NetServer extends Observable implements Runnable{
	
	private ServerSocket srvSocket;
	private int port;
	private Thread t;
	private Map<Socket, ObjectOutputStream> outputStreams;	// liste des outputstreams pour les clients
	
	
	/**
	 * Constructeur. Port par defaut: 8080
	 */
	public NetServer(){
		this(8080);
	}
	/**
	 * Constructeur
	 * @param port le port sur lequel le socket ecoute
	 */
	public NetServer(int port){
		this.port = port;
		this.t = new Thread(this);
		this.outputStreams = new HashMap<Socket, ObjectOutputStream>();
	}

	/**
	 * Demarre le thread d'ecoute
	 */
	public void start(){
		this.t.start();
	}
	/**
	 * stop le thread d'ecoute
	 */
	@SuppressWarnings("deprecation")
	public void stop(){
		this.t.stop();
	}

	/**
	 * Ouvre le socket
	 */
	public void openServerSocket() {
		try {
			this.srvSocket = new ServerSocket(this.port);
			this.start();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	/**
	 * Obtient les flux de sorties
	 * @return un map des flux de sortie par socket
	 */ 
	public Map<Socket, ObjectOutputStream> getOutputStreams(){
		return this.outputStreams;
	}
	
	/**
	 * Attend que des clients se connectent (action effectuee par le thread d'ecoute)
	 */
	public void waitForClients() {
		try {
			while (true) {
				Socket connection = srvSocket.accept();
				outputStreams.put(connection, new ObjectOutputStream(connection.getOutputStream()));
				// affichage de la nouvelle connection
				System.out.println("Connection received from "
						+ connection.getInetAddress().getHostName());
				@SuppressWarnings("unused")	// peut etre supprime car l'objet est actif (et "s'utilise donc tout seul")
				ClientListener cl = new ClientListener(connection);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Ferme le socket
	 */
	public void closeServerSocket() {
		try {
			srvSocket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Action du thread principal
	 */
	public void run(){
		this.waitForClients();
	}
	
	/**
	 * Un client qui ecoute
	 * @author Julien Roch
	 * @version 0.5
	 */
	class ClientListener implements Runnable {
		private Socket socket;
		private Thread thread;
		private ObjectInputStream in;

		/**
		 * Constructeur
		 * @param socket le socket ouvert lors de l'acceptation du client
		 */
		public ClientListener(Socket socket) {
			this.socket = socket;
			this.thread = new Thread(this);
			try {	// creation de l'interface d'ecoute pour le nouveau client
				in = new ObjectInputStream(socket.getInputStream());
			} catch (Exception e) {
				e.printStackTrace();
			}
			this.thread.start();
		}

		/**
		 * Methode d'ecoute du client (on recupere les objets qu'il envoie)
		 */
		public void run() {
			while (true) {
				try {
					Object o = in.readObject();
					// on essaie d'en faire des objets de comm.
					if(ComCliSrv.class.isInstance(o))
						((ComCliSrv)o).setSocket(this.socket);
					// on gere l'objet au moyen du handler
					synchronized (this) {
						setChanged();
						notifyObservers(o);
					}
				}catch (EOFException e){
					synchronized (this) {
						ClientDisconnect cd = new ClientDisconnect();
						outputStreams.remove(socket);	// on detache le socket
						cd.setSocket(socket);
						setChanged();
						notifyObservers(cd);		// on notifie de la deconnexion
					}
					try {
						socket.close();
					} catch (IOException e1) {
						e1.printStackTrace();	/** @debug */
					}
					return;
				} catch (Exception e) {
					e.printStackTrace(); /** @debug */
					return;
				}
			}
		}
	}
}


